﻿/*******************************************************************************

    Units of Measure for C# applications

    Copyright (C) 2013 Marek Aniola

    This program is provided to you under the terms of the license
    as published at http://unitsofmeasure.codeplex.com/license


********************************************************************************/
using System;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Man.UnitsOfMeasure;

namespace UnitsOfMeasure.Test
{
	[TestClass]
	public class LevelTests
	{
		private static SIUnitsDouble dsi = new SIUnitsDouble();
		private static SIUnitsFloat fsi = new SIUnitsFloat();
		private static SIUnitsDecimal msi = new SIUnitsDecimal();

		[TestClass]
		public class Conversions
		{
			[TestMethod]
			[ExpectedException(typeof(System.InvalidOperationException))]
			public void InconvertibleThrowException()
			{
				Level<double> celsius = dsi.Celsius.Level(100.0);	// 100 deg.C
				Level<double> celsiusInCalories = celsius[dsi.Calorie];
			}

			[TestMethod]
			public void TemperatureSamples()
			{
				// Water boils at:
				// http://en.wikipedia.org/wiki/Temperature_conversion_formulas#Comparison_of_temperature_scales
				{
					Level<double> celsius = dsi.Celsius.Level(100.0);
					Level<double> fahrenheit = celsius[dsi.Fahrenheit];	// == dsi.Fahrenheit[celsius];
					Level<double> rankine = fahrenheit[dsi.Rankine];	// == dsi.Rankine[fahrenheit];
					Level<double> kelvin = rankine[dsi.Kelvin];	// == dsi.Kelvin[rankine];
					celsius = kelvin[dsi.Celsius];	// == dsi.Celsius[kelvin];

					Assert.IsTrue(212.0 == fahrenheit.Value, "Celsius-to-Fahrenheit conversion (double) failed");
					Assert.IsTrue(671.67 == rankine.Value, "Fahrenheit-to-Rankine conversion (double) failed");
					Assert.IsTrue(373.15 == kelvin.Value, "Rankine-to-Kelvin conversion (double) failed");
					Assert.IsTrue(100.0 == celsius.Value, "Kelvin-to-Celsius conversion (double) failed");
				}
				{
					Level<float> celsius = fsi.Celsius.Level(100.0F);
					Level<float> fahrenheit = fsi.Fahrenheit[celsius];	// == celsius[fsi.Fahrenheit];
					Level<float> rankine = fsi.Rankine[fahrenheit];	// == fahrenheit[fsi.Rankine];
					Level<float> kelvin = fsi.Kelvin[rankine];	// == rankine[fsi.Kelvin];
					celsius = fsi.Celsius[kelvin];	// == kelvin[fsi.Celsius];

					Assert.IsTrue(211.999985F == fahrenheit.Value, "Celsius-to-Fahrenheit conversion (float) failed");
					Assert.IsTrue(671.67F == rankine.Value, "Fahrenheit-to-Rankine conversion (float) failed");
					Assert.IsTrue(373.15F == kelvin.Value, "Rankine-to-Kelvin conversion (float) failed");
					Assert.IsTrue(100.0F == celsius.Value, "Kelvin-to-Celsius conversion (float) failed");
				}
				{
					Level<decimal> celsius = msi.Celsius.Level(100.0M);
					Level<decimal> fahrenheit = celsius[msi.Fahrenheit];	// == msi.Fahrenheit[celsius];
					Level<decimal> rankine = fahrenheit[msi.Rankine];	// == msi.Rankine[fahrenheit];
					Level<decimal> kelvin = rankine[msi.Kelvin];	// == msi.Kelvin[rankine];
					celsius = kelvin[msi.Celsius];	// == msi.Celsius[kelvin];

					Assert.IsTrue(212.0M == fahrenheit.Value, "Celsius-to-Fahrenheit conversion (decimal) failed");
					Assert.IsTrue(671.67M == rankine.Value, "Fahrenheit-to-Rankine conversion (decimal) failed");
					Assert.IsTrue(373.15000000000000000000000003M == kelvin.Value, "Rankine-to-Kelvin conversion (decimal) failed");
					Assert.IsTrue(100.00000000000000000000000003M == celsius.Value, "Kelvin-to-Celsius conversion (decimal) failed");
				}
			}
		}

		[TestClass]
		public class ComaprisonOperators
		{
			[TestMethod]
			public void FloatingPointParanoia()
			{
				Assert.IsTrue(dsi.Fahrenheit.Level(123.45) == dsi.Fahrenheit.Level(123.45));
				Assert.IsTrue(msi.Fahrenheit.Level(123.45M) == msi.Fahrenheit.Level(123.45M));
				Assert.IsTrue(fsi.Fahrenheit.Level(123.45F) == fsi.Fahrenheit.Level(123.45F));
			}

			[TestMethod]
			public void EqGtGeLtLe()
			{
				{
					Level<double> celsius = dsi.Celsius.Level(100.0);
					Assert.IsTrue(celsius == dsi.Kelvin.Level(100.0 + 273.15));
					Assert.IsTrue(celsius > dsi.Kelvin.Level(100.0));
					Assert.IsTrue(celsius >= dsi.Kelvin.Level(100.0));
					Assert.IsTrue(dsi.Kelvin.Level(100.0) < celsius);
					Assert.IsTrue(dsi.Kelvin.Level(100.0) <= celsius);
				}
			}
		}

		[TestClass]
		public class AdditiveOperators
		{
			[TestMethod]
			public void AdditionSubtraction()
			{
				{
					Quantity<double> delta = dsi.Kelvin[5.0];

					Assert.IsTrue(dsi.Celsius.Level(100.0) + delta == dsi.Celsius.Level(105.0));
					Assert.IsTrue(dsi.Celsius.Level(105.0) - delta == dsi.Celsius.Level(100.0));
					Assert.IsTrue(dsi.Celsius.Level(105.0) - dsi.Celsius.Level(100.0) == delta);

					delta = dsi.Fahrenheit[9.0];

					Assert.IsTrue(dsi.Celsius.Level(100.0) + delta == dsi.Celsius.Level(105.0));
					Assert.IsTrue(delta + dsi.Celsius.Level(100.0) == dsi.Fahrenheit.Level(221.0));
					Assert.IsTrue(dsi.Celsius.Level(100.0) + delta == delta + dsi.Celsius.Level(100.0));
					Assert.IsTrue(dsi.Celsius.Level(105.0) - delta == dsi.Celsius.Level(100.0));
					Assert.IsTrue(dsi.Celsius.Level(105.0) - dsi.Celsius.Level(100.0) == delta);
				}
			}
		}

		[TestClass]
		public class MultiplicativeOperators
		{
			[TestMethod]
			public void ProductQoutient()
			{
				{
					Assert.IsTrue(2.0M * msi.Celsius.Level(15.0M) == msi.Celsius.Level(30.0M));
					Assert.IsTrue(msi.Celsius.Level(15.0M) * 2.0M == msi.Celsius.Level(30.0M));
					Assert.IsTrue(msi.Celsius.Level(30.0M) / 2.0M == msi.Celsius.Level(15.0M));
				}
			}

			[TestMethod]
			public void DivideByUnit()
			{
				Level<double> temperature = dsi.Celsius.Level(0.0);
				Assert.IsTrue(temperature / dsi.Kelvin == temperature[dsi.Kelvin].Value);
				Assert.IsTrue(dsi.Kelvin.Level(temperature / dsi.Kelvin) == temperature);
			}
		}

		[TestClass]
		public class Parsing
		{
			[TestMethod]
			public void TryParse()
			{
				// The below creates the string "3 125 000,00 °C" with PL-specific
				// NumberGroupSeparator that looks like space character but in fact it is
				// a non-breaking space that can't be easily entered from keyboard
				CultureInfo specificCulture = new CultureInfo("pl-PL");
				string localizedLevelWithGroupSeparator = String.Format(specificCulture, "{0:N} °C", 3125000.0);

				{
					Level<double> result;

					// OK with space separating number and unit
					Assert.IsTrue(dsi.TryParse("671.67 °R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == dsi.Rankine.Level(671.67));

					// OK w/o separating space
					Assert.IsTrue(dsi.TryParse("671.67°R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == dsi.Rankine.Level(671.67));

					// OK with lowercase unit, unless UnitSystem is case-sensitive
					Assert.IsTrue(dsi.TryParse("671.67°r", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == dsi.Rankine.Level(671.67));

					// Failure, unit not found
					Assert.IsFalse(dsi.TryParse("671.67 [°R]", CultureInfo.InvariantCulture, out result));

					// Failure, bad syntax (default parser looks for the unit to the right of the number)
					Assert.IsFalse(dsi.TryParse("°R 671.67", CultureInfo.InvariantCulture, out result));

					// OK with culture specific input
					Assert.IsTrue(dsi.TryParse(localizedLevelWithGroupSeparator, specificCulture, out result));
					Assert.IsTrue(result == dsi.Celsius.Level(3125000.0));
				}
				{
					Level<decimal> result;

					// OK with space separating number and unit
					Assert.IsTrue(msi.TryParse("671.67 °R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == msi.Rankine.Level(671.67M));

					// OK w/o separating space
					Assert.IsTrue(msi.TryParse("671.67°R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == msi.Rankine.Level(671.67M));

					// OK with lowercase unit, unless UnitSystem is case-sensitive
					Assert.IsTrue(msi.TryParse("671.67°r", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == msi.Rankine.Level(671.67M));

					// Failure, unit not found
					Assert.IsFalse(msi.TryParse("671.67 [°R]", CultureInfo.InvariantCulture, out result));

					// Failure, bad syntax (default parser looks for the unit to the right of the number)
					Assert.IsFalse(msi.TryParse("°R 671.67", CultureInfo.InvariantCulture, out result));

					// OK with culture specific input
					Assert.IsTrue(msi.TryParse(localizedLevelWithGroupSeparator, specificCulture, out result));
					Assert.IsTrue(result == msi.Celsius.Level(3125000.0M));
				}
				{
					Level<float> result;

					// OK with space separating number and unit
					Assert.IsTrue(fsi.TryParse("671.67 °R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == fsi.Rankine.Level(671.67F));

					// OK w/o separating space
					Assert.IsTrue(fsi.TryParse("671.67°R", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == fsi.Rankine.Level(671.67F));

					// OK with lowercase unit, unless UnitSystem is case-sensitive
					Assert.IsTrue(fsi.TryParse("671.67°r", CultureInfo.InvariantCulture, out result));
					Assert.IsTrue(result == fsi.Rankine.Level(671.67F));

					// Failure, unit not found
					Assert.IsFalse(fsi.TryParse("671.67 [°R]", CultureInfo.InvariantCulture, out result));

					// Failure, bad syntax (default parser looks for the unit to the right of the number)
					Assert.IsFalse(fsi.TryParse("°R 671.67", CultureInfo.InvariantCulture, out result));

					// OK with culture specific input
					Assert.IsTrue(fsi.TryParse(localizedLevelWithGroupSeparator, specificCulture, out result));
					Assert.IsTrue(result == fsi.Celsius.Level(3125000.0F));
				}
			}
		}
	}
}
